package com.study.aspect;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.study.common.BusinessException;
import com.study.log.annotation.Log;
import com.study.log.enums.LogType;
import com.study.log.event.LogEvent;
import com.study.log.repository.model.LogDO;
import com.study.security.domain.CurrentUser;
import com.study.security.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
 * @Description: 业务日志
 * @ClassName: LogAspect
 * @Author: hupengming
 * @CreateTime: 2022-08-25 18:16
 */
@Aspect
@Component
public class LogAspect {

    // 切入点
    /*@Pointcut(value = "execution(public * com.study.modules.controller.*.*(..))")
    public void pointCut(){}*/

    @Autowired
    ApplicationContext applicationContext;

    ThreadLocal<Long> currentTime = new ThreadLocal<>();

    @Around("@annotation(log)")
    public Object around(ProceedingJoinPoint joinPoint, Log log) throws Throwable {
        currentTime.set(System.currentTimeMillis());
        LogDO logDO = new LogDO();
        CurrentUser currentUser = SecurityUtils.getCurrentUser();
        // 标题
        logDO.setTitle(log.value());
        logDO.setToken(currentUser.getToken());
        logDO.setUserUuid(currentUser.getUuid());

        Object aThis = joinPoint.getThis();
        System.out.println("代理对象：" + aThis.getClass());

        Object target = joinPoint.getTarget();
        System.out.println("返回目标对象：" + target.getClass());

        // 类名
        String clazzName = joinPoint.getTarget().getClass().getName();
        logDO.setClassName(clazzName);

        // 方法名称
        String methodName = joinPoint.getSignature().getName();
        logDO.setMethodName(methodName);

        Object proceed = null;

        // 全部参数的值
        Object[] args = joinPoint.getArgs();
        try {
            // 方法执行完后的返回参数
            proceed = joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
            // 异常信息
            logDO.setExceptionDetail(e.getMessage());

            // 是否是业务异常
            if (e instanceof BusinessException) {
                BusinessException businessException = (BusinessException) e;
                // 业务异常
                logDO.setType(LogType.BUSSINESS_EXCEPTION);
                throw businessException;
            } else {
                logDO.setType(LogType.EXCEPTION);
                throw e;
            }
        } finally {
            HttpServletRequest request = ((ServletRequestAttributes)
                    Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();

            if (request != null) {
                // 获得ip地址
                logDO.setIp(ServletUtil.getClientIP(request));
            }

            // 设置耗费时间
            logDO.setTimed(System.currentTimeMillis() - currentTime.get());

            List<Object> argList = new ArrayList<>();

            MethodSignature signatures = (MethodSignature) joinPoint.getSignature();

            // 获取方法参数名
            Parameter[] parameters = signatures.getMethod().getParameters();

            for (int i = 0; i < parameters.length; i++) {

                // 请求路径上得参数
                PathVariable pathVariable = parameters[i].getAnnotation(PathVariable.class);
                if (ObjectUtil.isNotNull(pathVariable)) {
                    argList.add(args[i]);
                }

                // 请求体中得参数
                RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
                if (ObjectUtil.isNotNull(requestBody)) {
                    argList.add(args[i]);
                }

                // 请求路径上得参数
                RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
                if (ObjectUtil.isNotNull(requestParam)) {
                    Map<String, Object> map = new HashMap<>();
                    if (StringUtils.isNotEmpty(requestParam.value())) {
                        String key = requestParam.value();
                        map.put(key, args[i]);
                        argList.add(map);
                    }
                }
            }
            if (argList.size() > 0) {
                logDO.setParams(JSON.toJSONString(argList));
            }
        }
        applicationContext.publishEvent(new LogEvent(logDO));
        return proceed;
    }
}
